/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util;
import java.awt.EventQueue;
import java.util.*;
import java.util.Map.Entry;
import javax.swing.SwingUtilities;
/** Read-many/write-one lock.
* Allows control over resources that
* can be read by several readers at once but only written by one writer.
* <P>
* It is guaranteed that if you are a writer you can also enter the
* mutex as a reader. Conversely, if you are the <em>only</em> reader you
* are allowed to enter the mutex as a writer.
* <P>
* If the mutex is used only by one thread, the thread can repeatedly
* enter it as a writer or reader. So one thread can never deadlock itself,
* whichever order operations are performed in.
* <P>
* There is no strategy to prevent starvation.
* Even if there is a writer waiting to enter, another reader might enter
* the section instead.
* <P>
* Examples of use:
*
* <p><code><PRE>
* Mutex m = new Mutex ();
*
* // Grant write access, compute an integer and return it:
* return (Integer)m.writeAccess (new Mutex.Action () {
* public Object run () {
* return new Integer (1);
* }
* });
*
* // Obtain read access, do some computation, possibly throw an IOException:
* try {
* m.readAccess (new Mutex.ExceptionAction () {
* public Object run () throws IOException {
* if (...) throw new IOException ();
*
* return null;
* }
* });
* } catch (MutexException ex) {
* throw (IOException)ex.getException ();
* }
* </PRE></code>
*
* @author Ales Novak
*/
public final class Mutex extends Object {
/** Mutex that allows code to be synchronized with the AWT event dispatch thread. */
public static final Mutex EVENT = new Mutex (null);
// lock mode constants
/** Lock free */
private static final int NONE = 0x0;
/** Enqueue all requests */
private static final int CHAIN = 0x1;
/** eXclusive */
private static final int X = 0x2;
/** Shared */
private static final int S = 0x3;
/** number of modes */
private static final int MODE_COUNT = 0x4;
/** timeout for awttreelock holder */
private static final long TIMEOUT = 30000L;
/** compatibility matrix */ // [requested][granted]
private static final boolean[][] cmatrix = {null, null, // NONE, CHAIN
/* NONE */ /* CHAIN */ /* X */ /* S */ // granted
/*r X */ {true, false, false, false},
/*e S */ {true, false, false, true}
/*q */
//uested
};
/** Decides whether two locks are compatible.�
* @param granted�
* @param requested�
* @return <tt>true</tt> iff they are compatible�
*/
private static boolean compatibleLocks(int granted, int requested) {
return cmatrix[requested][granted];
}
/** granted mode */
private int grantedMode = NONE;
/** protects internal data structures */
private /*final*/ Object LOCK;
/** threads that - owns or waits for this mutex
* @associates ThreadInfo*/
private /*final*/ Map registeredThreads;
/** number of threads that holds S mode (readersNo == "count of threads in registeredThreads that holds S") */ // NOI18N
private int readersNo = 0;
/** a queue of waiting threads for this mutex
* @associates QueueCell*/
private List waiters;
/** Enhanced constructor that permits specifying an object to use as a lock.
* The lock is used on entry and exit to {@link #readAccess} and during the
* whole execution of {@link #writeAccess}. The ability to specify locks
* allows several <code>Mutex</code>es to synchronize on one object or to synchronize
* a mutex with another critical section.
*
* @param lock lock to use
*/
public Mutex (Object lock) {
this.LOCK = lock;
this.registeredThreads = new HashMap(7);
this.waiters = new LinkedList();
}
/** Default constructor.
*/
public Mutex() {
this(new InternalLock());
}
/** Run an action only with read access.
* See class description re. entering for write access within the dynamic scope.
* @param action the action to perform
* @return the object returned from {@link Mutex.Action#run}
*/
public Object readAccess (Action action) {
if (this == EVENT) {
try {
return doEventAccess (action);
} catch (MutexException e) {
throw new InternalError();
}
}
Thread t = Thread.currentThread();
readEnter(t);
try {
return action.run();
} finally {
leave(t);
}
}
/** Run an action with read access and possibly throw a checked exception.
* The exception if thrown is then encapsulated
* in a <code>MutexException</code> and thrown from this method. One is encouraged
* to catch <code>MutexException</code>, obtain the inner exception, and rethrow it.
* Here is an example:
* <p><code><PRE>
* try {
* mutex.readAccess (new ExceptionAction () {
* public void run () throws IOException {
* throw new IOException ();
* }
* });
* } catch (MutexException ex) {
* throw (IOException) ex.getException ();
* }
* </PRE></code>
* Note that <em>runtime exceptions</em> are always passed through, and neither
* require this invocation style, nor are encapsulated.
* @param action the action to execute
* @return the object returned from {@link Mutex.ExceptionAction#run}
* @exception MutexException encapsulates a user exception
* @exception RuntimeException if any runtime exception is thrown from the run method
* @see #readAccess(Mutex.Action)
*/
public Object readAccess (ExceptionAction action) throws MutexException {
if (this == EVENT) {
return doEventAccess (action);
}
Thread t = Thread.currentThread();
readEnter(t);
try {
return action.run();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new MutexException(e);
} finally {
leave(t);
}
}
/** Run an action with read access, returning no result.
* It may be run asynchronously.
*
* @param action the action to perform
* @see #readAccess(Mutex.Action)
*/
public void readAccess (final Runnable action) {
if (this == EVENT) {
doEvent (action);
return;
}
Thread t = Thread.currentThread();
readEnter(t);
try {
action.run();
} finally {
leave(t);
}
}
/** Run an action with write access.
* The same thread may meanwhile reenter the mutex; see the class description for details.
*
* @param action the action to perform
* @return the result of {@link Mutex.Action#run}
*/
public Object writeAccess (Action action) {
if (this == EVENT) {
try {
return doEventAccess (action);
} catch (MutexException e) {
throw new InternalError();
}
}
Thread t = Thread.currentThread();
writeEnter(t);
try {
return action.run();
} finally {
leave(t);
}
}
/** Run an action with write access and possibly throw an exception.
* Here is an example:
* <p><code><PRE>
* try {
* mutex.writeAccess (new ExceptionAction () {
* public void run () throws IOException {
* throw new IOException ();
* }
* });
* } catch (MutexException ex) {
* throw (IOException) ex.getException ();
* }
* </PRE></code>
*
* @param action the action to execute
* @return the result of {@link Mutex.ExceptionAction#run}
* @exception MutexException an encapsulated checked exception, if any
* @exception RuntimeException if a runtime exception is thrown in the action
* @see #writeAccess(Mutex.Action)
* @see #readAccess(Mutex.ExceptionAction)
*/
public Object writeAccess (ExceptionAction action) throws MutexException {
if (this == EVENT) {
return doEventAccess (action);
}
Thread t = Thread.currentThread();
writeEnter(t);
try {
return action.run();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new MutexException(e);
} finally {
leave(t);
}
}
/** Run an action with write access and return no result.
* It may be run asynchronously.
*
* @param action the action to perform
* @see #writeAccess(Mutex.Action)
* @see #readAccess(Runnable)
*/
public void writeAccess (final Runnable action) {
if (this == EVENT) {
doEvent (action);
return;
}
Thread t = Thread.currentThread();
writeEnter(t);
try {
action.run();
} finally {
leave(t);
}
}
/** Not implemented
*
* @param run runnable to run
* @deprecated
*/
public void postReadRequest (final Runnable run) {
postRequest(S, run);
}
/** Run an action with write access as soon as that is permitted.
* If there is no writer & reader, it will be run immediately;
* otherwise it will be run later when the last writer and/or reader finishes.
*
* @param run runnable to run
*/
public void postWriteRequest (Runnable run) {
postRequest(X, run);
}
/** toString */
public String toString() {
String newline = System.getProperty("line.separator");
StringBuffer sbuff = new StringBuffer(512);
synchronized(LOCK){
sbuff.append("threads: ").append(registeredThreads).append(newline); // NOI18N
sbuff.append("readersNo: ").append(readersNo).append(newline); // NOI18N
sbuff.append("waiters: ").append(waiters).append(newline); // NOI18N
sbuff.append("grantedMode: ").append(grantedMode).append(newline); // NOI18N
}
return sbuff.toString();
}
// priv methods -----------------------------------------
/** enters this mutex for writing */
private void writeEnter(Thread t) {
enter(X, t, true);
}
/** enters this mutex for reading */
private void readEnter(Thread t) {
enter(S, t, true);
}
/** enters this mutex with given mode
* @param requested one of S, X
* @param t
*/
private boolean enter(int requested, Thread t, boolean block) {
QueueCell cell = null;
for (;;) {
synchronized (LOCK) {
// System.out.println("ENTER: " + t + " mode: " + requested + " granted: " + grantedMode); // NOI18N
// does the thread reenter this mutex?
ThreadInfo info = getThreadInfo(t);
if (info != null) {
if (grantedMode == NONE) {
// defensive
throw new IllegalStateException();
}
// reenters
// requested == S -> always succeeds
// info.mode == X -> always succeeds
if (((info.mode == S) && (grantedMode == X)) ||
((info.mode == X) && (grantedMode == S))) {
// defensive
throw new IllegalStateException();
}
if ((info.mode == X) ||
(info.mode == requested)) { // X - X, X - S, S - S
if (info.forced) {
info.forced = false;
} else {
info.counts[requested]++;
if ((requested == S) && (info.counts[requested] == 1)) {
readersNo++;
}
}
return true;
} else if (canUpgrade(info.mode, requested)) { // S - X and no holders
info.mode = X;
info.counts[requested]++;
info.rsnapshot = info.counts[S];
if (grantedMode == S) {
grantedMode = X;
} else if (grantedMode == X) {
// defensive
throw new IllegalStateException();
} // else if grantedMode == CHAIN - let it be
return true;
} else { // S - X and holders
if (Boolean.getBoolean("netbeans.debug.threads")) { // NOI18N
System.err.println("WARNING: Going from readAccess to writeAccess");
Thread.dumpStack();
}
// chain follows
}
} else { // first acquisition
if (isCompatible(requested)) { // NONE -> S,X or S -> S
grantedMode = requested;
registeredThreads.put(t, new ThreadInfo(t, requested));
if (requested == S) {
readersNo++;
}
return true;
} // else {
// granted is S and requested is X
// granted is X and requested is S or X
//}
}
if (! block) {
return false;
}
grantedMode = CHAIN;
cell = chain(requested, t);
// System.out.println(System.identityHashCode(this) + " ENTEREND"); // NOI18N
} // sync
cell.sleep();
} // for
}
/** Leaves this mutex */
private void leave(Thread t) {
ThreadInfo info;
synchronized (LOCK) {
info = getThreadInfo(t);
switch (grantedMode) {
case NONE:
throw new IllegalStateException();
case CHAIN:
if (info.counts[X] > 0) {
// it matters that X is handled first - see ThreadInfo.rsnapshot
leaveX(info);
} else if (info.counts[S] > 0) {
leaveS(info);
} else {
throw new IllegalStateException();
}
break;
case X:
leaveX(info);
break;
case S:
leaveS(info);
break;
} // switch
} // sync
// check posted requests
if (info.mode == NONE) { // last enter to mutex being left
List runnables = info.dequeue();
if (runnables != null) { // pending requests
final int size = runnables.size();
for (int i = 0; i < size; i++) {
try {
Runnable r = (Runnable) runnables.get(i);
writeAccess(r);
} catch (ThreadDeath td) {
throw td;
} catch (Throwable e) {
if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
e.printStackTrace();
}
} // try
} // for
}
} // mode
}
/** Leaves the lock supposing that info.counts[X] is greater than zero */
private void leaveX(ThreadInfo info) {
if ((info.counts[X] <= 0) ||
(info.rsnapshot > info.counts[S])) {
// defensive
throw new IllegalStateException();
}
if (info.rsnapshot == info.counts[S]) {
info.counts[X]--;
if (info.counts[X] == 0) {
info.rsnapshot = 0;
// downgrade the lock
if (info.counts[S] > 0) {
info.mode = grantedMode = S;
} else {
info.mode = grantedMode = NONE;
registeredThreads.remove(info.t);
}
// mode has changed
wakeUpOthers();
}
} else {
// rsnapshot < counts[S]
if (info.counts[S] <= 0) {
// defensive
throw new IllegalStateException();
}
if (--info.counts[S] == 0) {
if (readersNo <= 0) {
throw new IllegalStateException();
}
readersNo--;
}
}
}
/** Leaves the lock supposing that info.counts[X] is greater than zero */
private void leaveS(ThreadInfo info) {
if ((info.counts[S] <= 0) ||
(info.counts[X] > 0)) {
// defensive
throw new IllegalStateException();
}
info.counts[S]--;
if (info.counts[S] == 0) {
// remove the thread
info.mode = NONE;
registeredThreads.remove(info.t);
// downsize readersNo
if (readersNo <= 0) {
throw new IllegalStateException();
}
readersNo--;
if (readersNo == 0) {
grantedMode = NONE;
wakeUpOthers();
} else if ((grantedMode == CHAIN) &&
(readersNo == 1)) {
// can be the mode advanced from CHAIN? Examine first item of waiters!
for (int i = 0; i < waiters.size(); i++) {
QueueCell qc = (QueueCell) waiters.get(i);
synchronized (qc) {
if (qc.isGotOut()) {
waiters.remove(i--);
continue;
}
ThreadInfo tinfo = getThreadInfo(qc.t);
if (tinfo != null) {
if (tinfo.mode == S) {
if (qc.mode != X) {
// defensive
throw new IllegalStateException();
}
if (waiters.size() == 1) {
grantedMode = X;
} // else let CHAIN
tinfo.mode = X;
waiters.remove(i);
qc.wakeMeUp();
}
} // else first request is a first X request of some thread
break;
} // sync (qc)
} // for
} // else
} // count[S] == 0
}
/** Adds this thread to the queue of waiting threads
* @warning LOCK must be held
*/
private QueueCell chain(int requested, Thread t) {
long timeout = 0;
if (killDeadlocksOn) {
checkDeadlock(requested, t);
timeout = (isDispatchThread() || checkAwtTreeLock() ? TIMEOUT : 0);
}
QueueCell qc = new QueueCell(requested, t);
qc.timeout = timeout;
final int size = waiters.size();
if (size == 0) {
waiters.add(qc);
} else {
QueueCell cursor;
int i = 0;
do {
cursor = (QueueCell) waiters.get(i);
if (cursor.getPriority() < qc.getPriority()) {
waiters.add(i, qc);
break;
}
i++;
} while (i < size);
if (i == size) {
waiters.add(qc);
}
}
return qc;
}
/** Scans through waiters and wakes up them */
private void wakeUpOthers() {
if ((grantedMode == X) ||
(grantedMode == CHAIN)) {
// defensive
throw new IllegalStateException();
}
if (waiters.size() == 0) {
return;
}
for (int i = 0; i < waiters.size(); i++) {
QueueCell qc = (QueueCell) waiters.get(i);
synchronized (qc) {
if (qc.isGotOut()) {
// bogus waiter
waiters.remove(i--);
continue;
}
if (compatibleLocks(grantedMode, qc.mode)) { // woken S -> should I wake X? -> no
waiters.remove(i--);
qc.wakeMeUp();
grantedMode = qc.mode;
if (getThreadInfo(qc.t) == null) {
// force to have a record since recorded threads
// do not use isCompatible call
ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
ti.forced = true;
if (qc.mode == S) {
readersNo++;
}
registeredThreads.put(qc.t, ti);
}
} else {
grantedMode = CHAIN;
break;
}
} // sync (qc)
}
}
/** Posts new request for current thread
* @param mutexMode mutex mode for which the action is rquested
* @param run the action
*/
private void postRequest(int mutexMode, Runnable run) {
if (this == EVENT) {
doEventRequest(run);
return;
}
Thread t = Thread.currentThread();
ThreadInfo info;
synchronized (LOCK) {
info = getThreadInfo(t);
if (info != null) {
if (mutexMode == info.mode) {
enter(mutexMode, t, true);
} else { // the mutex is held but can not be entered in X mode
info.enqueue(run);
return;
}
}
}
// this mutex is not held
if (info == null) {
enter(mutexMode, t, true);
try {
run.run();
} finally {
leave(t);
}
return;
}
// run it immediately
// info != null so enter(...) succeeded
try {
run.run();
} finally {
leave(t);
}
}
/** @param requested is requested mode of locking
* @return <tt>true</tt> if and only if current mode and requested mode are compatible
*/
private boolean isCompatible(int requested) {
return compatibleLocks(grantedMode, requested);
}
private ThreadInfo getThreadInfo(Thread t) {
return (ThreadInfo) registeredThreads.get(t);
}
private boolean canUpgrade(int threadGranted, int requested) {
return (threadGranted == S) && (requested == X) && (readersNo == 1);
}
// deadlock detector vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
/** Simple deadlock detector */
private void checkDeadlock(int requested, Thread t) {
if (grantedMode == CHAIN) {
Set roots = getOwners();
if (roots == null) {
return;
}
if (! roots.contains(t)) {
return; // new thread
}
// we know requested == X now - since t is in roots
int size = waiters.size();
if (size == 0) {
return;
}
QueueCell qc = (QueueCell) waiters.get(0);
if (qc.mode == S) {
// defensive
throw new IllegalStateException();
} else {
// t is in roots
// and a thread is in the waiters queue
// the thread waits for completition
// deadlock
throwDeadlockAvoidance();
}
}
}
/** @return a Set of threads that are registered - owns this lock
* In X case returns null;
* In S case it can be several threads
* (however grantedMode is CHAIN)
*/
private Set getOwners() {
int mode = 0;
Iterator regs = registeredThreads.entrySet().iterator();
Set ret = new HashSet((int) (1.3 * registeredThreads.size()));
while (regs.hasNext()) {
Entry e = (Entry) regs.next();
if (((ThreadInfo) e.getValue()).mode == X) {
if (mode == S) {
// defensive
throw new IllegalStateException();
}
return null;
} else {
mode = S;
}
ret.add(e.getKey());
}
return ret;
}
/** AWTTreeLock (java.awt.Component.getTreeLock) */
private static Object TREELOCK;
/** @return true if the check succeed, that is, if current thread holds AWTTreeLock */
private boolean checkAwtTreeLock() {
if (TREELOCK == null) {
TREELOCK = new java.awt.Panel().getTreeLock();
}
for (;;) {
try {
TREELOCK.notify();
// oh god I gotcha
return true;
} catch (IllegalMonitorStateException e) {
return false; // OK do not have it
}
}
}
/** Never returns normally - always throws DeadlockAvoidanceException */
private void throwDeadlockAvoidance() {
if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
System.err.println(toString());
}
throw new DeadlockAvoidanceException();
}
// deadlock detector ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ------------------------------- EVENT METHODS ----------------------------
/** Runs the runnable in event queue, either immediatelly,
* or it posts it into the queue.
*/
private static void doEvent (Runnable run) {
if (EventQueue.isDispatchThread ()) {
run.run ();
} else {
EventQueue.invokeLater (run);
}
}
/** Methods for access to event queue.
* @param run runabble to post later
*/
private static void doEventRequest (Runnable run) {
EventQueue.invokeLater (run);
}
/** Methods for access to event queue and waiting for result.
* @param run runabble to post later
*/
private static Object doEventAccess (final ExceptionAction run) throws MutexException {
if (isDispatchThread()) {
try {
return run.run ();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new MutexException (e);
}
}
final Exception[] arr = new Exception[1];
try {
final Object[] res = new Object[1];
EventQueue.invokeAndWait (new Runnable () {
public void run () {
try {
res[0] = run.run ();
} catch (Exception e) {
arr[0] = e;
}
}
});
return res[0];
} catch (InterruptedException e) {
arr[0] = e;
} catch (java.lang.reflect.InvocationTargetException e) {
arr[0] = (Exception)e.getTargetException ();
}
if (arr[0] instanceof RuntimeException) {
throw (RuntimeException)arr[0];
}
throw new MutexException (arr[0]);
}
/** @return true iff current thread is EventDispatchThread */
static boolean isDispatchThread() {
boolean dispatch = EventQueue.isDispatchThread ();
if (!dispatch && Utilities.getOperatingSystem () == Utilities.OS_SOLARIS) {
// on solaris the event queue is not always recognized correctly
// => try to guess by name
dispatch = (Thread.currentThread().getClass().getName().indexOf("EventDispatchThread") >= 0); // NOI18N
}
return dispatch;
}
// --------------------------------------------- END OF EVENT METHODS ------------------------------
/** Action to be executed in a mutex without throwing any checked exceptions.
* Unchecked exceptions will be propagated to calling code.
*/
public static interface Action extends ExceptionAction {
/** Execute the action.
* @return any object, then returned from {@link Mutex#readAccess(Mutex.Action) or {@link Mutex#writeAccess(Mutex.Action)}
*/
public Object run ();
}
/** Action to be executed in a mutex, possibly throwing checked exceptions.
* May throw a checked exception, in which case calling
* code should catch the encapsulating exception and rethrow the
* real one.
* Unchecked exceptions will be propagated to calling code without encapsulation.
*/
public static interface ExceptionAction {
/** Execute the action.
* Can throw an exception.
* @return any object, then returned from {@link Mutex#readAccess(Mutex.ExceptionAction) or {@link Mutex#writeAccess(Mutex.ExceptionAction)}
* @exception Exception any exception the body needs to throw
*/
public Object run () throws Exception;
}
private static class ThreadInfo {
/** t is forcibly sent from waiters to enter() by wakeUpOthers() */
boolean forced;
/** ThreadInfo for this Thread */
final Thread t;
/** granted mode */
int mode;
// 0 - NONE, 1 - CHAIN, 2 - X, 3 - S
/** enter counter */
int[] counts;
/** queue of runnable rquests that are to be executed (in X mode) right after S mode is left
* deadlock avoidance technique
*/
List[] queues;
/** value of counts[S] when the mode was upgraded
* rsnapshot works as follows:
* if a thread holds the mutex in the S mode and it reenters the mutex
* and requests X and the mode can be granted (no other readers) then this
* variable is set to counts[S]. This is used in the leave method in the X branch.
* (X mode is granted by other words)
* If rsnapshot is less than counts[S] then the counter is decremented etc. If the rsnapshot is
* equal to count[S] then count[X] is decremented. If the X counter is zeroed then
* rsnapshot is zeroed as well and current mode is downgraded to S mode.
* rsnapshot gets less than counts[S] if current mode is X and the mutex is reentered
* with S request.
*/
int rsnapshot;
public ThreadInfo(Thread t, int mode) {
this.t = t;
this.mode = mode;
this.counts = new int[MODE_COUNT];
this.queues = new List[MODE_COUNT];
counts[mode] = 1;
}
public String toString() {
return super.toString() + " thread: " + t + " mode: " + mode + " X: " + counts[2] + " S: " + counts[3]; // NOI18N
}
/** Adds the Runnable into the queue of waiting requests */
public void enqueue(Runnable run) {
if (queues[X] == null) {
queues[X] = new ArrayList(13);
}
queues[X].add(run);
}
/** @return a List of enqueued Runnables - may be null */
public List dequeue() {
List ret = queues[X];
queues[X] = null;
return ret;
}
}
/** This class is defined only for better understanding of thread dumps where are informations like
* java.lang.Object@xxxxxxxx owner thread_x
* wait for enter thread_y
*/
private static class InternalLock {
}
private class QueueCell {
int mode;
Thread t;
boolean signal;
/** if the thread is owner of AWTTreeLock then the timeout is greater than zero */
long timeout;
boolean left;
public QueueCell(int mode, Thread t) {
this.mode = mode;
this.t = t;
this.timeout = 0;
this.left = false;
}
public String toString() {
return super.toString() + " mode: " + mode + " thread: " + t; // NOI18N
}
/** @return priority of this cell */
public int getPriority() {
return t.getPriority();
}
/** @return true iff the thread left sleep */
public boolean isGotOut() {
return left;
}
/** current thread will sleep until wakeMeUp is called
* if wakeMeUp was already called then the thread will not sleep
*/
public synchronized void sleep() {
try {
if (! signal) {
for (;;) {
try {
wait(timeout);
if ((timeout > 0) && !signal) {
// timed out
throwDeadlockAvoidance();
}
return;
} catch (InterruptedException e) { // IMHO should be propagated to r/wAccess(action)
if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
e.printStackTrace();
}
}
}
}
} finally {
left = true;
}
}
/** sends signal to a sleeper - to a thread that is in the sleep() */
public void wakeMeUp() {
signal = true;
notify();
}
}
/** Thrown when a deadlock appears. */
static final class DeadlockAvoidanceException extends RuntimeException {
static final long serialVersionUID =8409658204844386128L;
public DeadlockAvoidanceException() {
super ();
}
}
/** if <tt>true</tt> then deadlock avoidance is switched on */
private static final boolean killDeadlocksOn = false;
}
/*
* Log
* 27 Gandalf 1.26 1/13/00 Ian Formanek NOI18N
* 26 Gandalf 1.25 1/12/00 Pavel Buzek I18N
* 25 Gandalf 1.24 12/20/99 Ales Novak getHoldingMode methods
* removed
* 24 Gandalf 1.23 12/14/99 Ales Novak posting of runnables
* 23 Gandalf 1.22 12/14/99 Ales Novak
* 22 Gandalf 1.21 12/10/99 Ales Novak error
* 21 Gandalf 1.20 12/10/99 Ales Novak getHoldingMode methods
* added
* 20 Gandalf 1.19 11/26/99 Patrik Knakal
* 19 Gandalf 1.18 11/2/99 Ales Novak better synchronization &
* bugfix
* 18 Gandalf 1.17 10/27/99 Ales Novak new implementation of
* this class
* 17 Gandalf 1.16 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 16 Gandalf 1.15 9/2/99 Jaroslav Tulach Runs what is posted as
* run later.
* 15 Gandalf 1.14 9/2/99 Jaroslav Tulach Exclusive access is
* really exclusive.
* 14 Gandalf 1.13 9/1/99 Jaroslav Tulach No debug messages.
* 13 Gandalf 1.12 9/1/99 Jaroslav Tulach Mutex.postWriteRequest
* 12 Gandalf 1.11 8/30/99 Jaroslav Tulach postWriteRequest
* 11 Gandalf 1.10 8/27/99 Jaroslav Tulach New threading model &
* Children.
* 10 Gandalf 1.9 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 9 Gandalf 1.8 6/7/99 Jaroslav Tulach Try to solve #1858
* 8 Gandalf 1.7 5/14/99 Jesse Glick [JavaDoc]
* 7 Gandalf 1.6 4/20/99 Jaroslav Tulach Children supports weak
* references.
* 6 Gandalf 1.5 4/19/99 Jesse Glick [JavaDoc]
* 5 Gandalf 1.4 2/11/99 Jaroslav Tulach No more postReadRequests.
* 4 Gandalf 1.3 2/4/99 Jaroslav Tulach Children.MUTEX
* synchronizes on Component Tree Lock.
* 3 Gandalf 1.2 2/3/99 Jaroslav Tulach invokeSafely
* 2 Gandalf 1.1 1/29/99 Ales Novak
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
*/